#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#include "sample_comm.h"
#include "audio_aac_adp.h"

#include "hi_appcomm.h"
#include "hi_ext_util.h"
#include "plug_media_ai.h"

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif

typedef struct tagPLUG_MEDIA_AI_CONTEXT
{

    HI_BOOL bInit;
    HI_BOOL bStart;
    pthread_t tAiSendId;
    
    PLUG_MEDIA_AI_INFO stAiInfo; /* ai info */
    
    AUDIO_DEV AiDev;
    AI_CHN      AiChn;
    AENC_CHN    AeChn;
    HI_S32      s32AiChnCnt;
    HI_S32      s32AencChnCnt;
}PLUG_MEDIA_AI_CONTEXT;

static PLUG_MEDIA_AI_CONTEXT s_stAiCtx = {.bInit = HI_FALSE, .AiDev = SAMPLE_AUDIO_INNER_AI_DEV, .AiChn = -1, \
                    .AeChn = -1, .s32AiChnCnt = -1, .s32AencChnCnt = -1};

static HI_VOID* MEDIA_AI_Thread(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;

    HI_S32 AencFd;
    AUDIO_STREAM_S stStream;
    fd_set read_fds;
    struct timeval TimeoutVal;

    FD_ZERO(&read_fds);
    AencFd = HI_MPI_AENC_GetFd(s_stAiCtx.AeChn);
    FD_SET(AencFd, &read_fds);
    
    PLUG_COMMON_AI_DATA_INFO stData;

    while (s_stAiCtx.bStart)
    {
        TimeoutVal.tv_sec = 1;
        TimeoutVal.tv_usec = 0;

        FD_ZERO(&read_fds);
        FD_SET(AencFd, &read_fds);

        s32Ret = select(AencFd + 1, &read_fds, NULL, NULL, &TimeoutVal);
        if (s32Ret < 0)
        {
            break;
        }
        else if (0 == s32Ret)
        {
            printf("%s: get aenc stream select time out\n", __FUNCTION__);
            break;
        }

        if (FD_ISSET(AencFd, &read_fds))
        {
            /* get stream from aenc chn */
            s32Ret = HI_MPI_AENC_GetStream(s_stAiCtx.AeChn, &stStream, HI_FALSE);
            if (HI_SUCCESS != s32Ret )
            {
                printf("%s: HI_MPI_AENC_GetStream(%d), failed with %#x!\n", \
                       __FUNCTION__, s_stAiCtx.AeChn, s32Ret);
                break;
            }
            
            stData.pu8Buff = stStream.pStream;
            stData.s32Bufflen = stStream.u32Len;
            stData.u64Pts = stStream.u64TimeStamp;

            s_stAiCtx.stAiInfo.pfnRecMsg(&stData);
            s_stAiCtx.stAiInfo.pfnRecordMsg(&stData);

            /* finally you must release the stream */
            s32Ret = HI_MPI_AENC_ReleaseStream(s_stAiCtx.AeChn, &stStream);
            if (HI_SUCCESS != s32Ret )
            {
                printf("%s: HI_MPI_AENC_ReleaseStream(%d), failed with %#x!\n", \
                       __FUNCTION__, s_stAiCtx.AeChn, s32Ret);
                break;
            }
        }
    }

    s_stAiCtx.bStart = HI_FALSE;
    return NULL;
}

HI_S32 PLUG_MEDIA_AI_Init(PLUG_MEDIA_AI_INFO stAiInfo)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    HI_S32 i, j;

    if (s_stAiCtx.bInit)
    {
        LOGE("ai has init\n");
        return HI_SUCCESS;
    }
    
    memcpy(&s_stAiCtx.stAiInfo, &stAiInfo, sizeof(s_stAiCtx.stAiInfo));
    
    s_stAiCtx.AiDev = SAMPLE_AUDIO_INNER_AI_DEV;
    
    AIO_ATTR_S stAioAttr;
    stAioAttr.enSamplerate   = AUDIO_SAMPLE_RATE_48000;
    stAioAttr.enBitwidth     = AUDIO_BIT_WIDTH_16;
    stAioAttr.enWorkmode     = AIO_MODE_I2S_MASTER;
    stAioAttr.enSoundmode    = AUDIO_SOUND_MODE_MONO; // AUDIO_SOUND_MODE_STEREO;
    stAioAttr.u32EXFlag      = 0;
    stAioAttr.u32FrmNum      = 30;
    stAioAttr.u32PtNumPerFrm = AACLC_SAMPLES_PER_FRAME;
    stAioAttr.u32ChnCnt      = 1; // 2;
    stAioAttr.u32ClkSel      = 1;
    stAioAttr.enI2sType      = AIO_I2STYPE_INNERCODEC;
    
    s32Ret = HI_MPI_AENC_AacInit();
    HI_APPCOMM_CHECK_RETURN(s32Ret, s32Ret);
    
    /********************************************
      step 1: start Ai
    ********************************************/
    s_stAiCtx.s32AiChnCnt = stAioAttr.u32ChnCnt;
    s32Ret = SAMPLE_COMM_AUDIO_StartAi(s_stAiCtx.AiDev, s_stAiCtx.s32AiChnCnt, &stAioAttr, \
            AUDIO_SAMPLE_RATE_BUTT, HI_FALSE, NULL, 0);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StartAi failed![%d]\n", s32Ret);
        goto AIAENC_ERR6;
    }

    /********************************************
      step 2: config audio codec
    ********************************************/
    s32Ret = SAMPLE_COMM_AUDIO_CfgAcodec(&stAioAttr);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StartAi failed![%d]\n", s32Ret);
        goto AIAENC_ERR5;
    }
    
    /********************************************
      step 3: start Aenc
    ********************************************/
    s_stAiCtx.s32AencChnCnt = stAioAttr.u32ChnCnt >> stAioAttr.enSoundmode;
    s32Ret = SAMPLE_COMM_AUDIO_StartAenc(s_stAiCtx.s32AencChnCnt, &stAioAttr, PT_AAC);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StartAenc failed![%d]\n", s32Ret);
        goto AIAENC_ERR5;
    }

    /********************************************
      step 4: Aenc bind Ai Chn
    ********************************************/
    for (i = 0; i < s_stAiCtx.s32AencChnCnt; i++)
    {
        s_stAiCtx.AeChn = i;
        s_stAiCtx.AiChn = i;
        
        s32Ret = SAMPLE_COMM_AUDIO_AencBindAi(s_stAiCtx.AiDev, s_stAiCtx.AiChn, s_stAiCtx.AeChn);
        if (s32Ret != HI_SUCCESS)
        {
            LOGE("SAMPLE_COMM_AUDIO_AencBindAi failed![%d]\n", s32Ret);
            for (j=0; j<i; j++)
            {
                SAMPLE_COMM_AUDIO_AencUnbindAi(s_stAiCtx.AiDev, j, j);
            }
            goto AIAENC_ERR4;
        }
        LOGI("Ai(%d,%d) bind to AencChn:%d ok!\n", s_stAiCtx.AiDev , s_stAiCtx.AiChn, s_stAiCtx.AeChn);
    }
    
    s_stAiCtx.bInit = HI_TRUE;
    
    return HI_SUCCESS;
    
AIAENC_ERR4:
    s32Ret |= SAMPLE_COMM_AUDIO_StopAenc(s_stAiCtx.s32AencChnCnt);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAenc failed![%d]\n", s32Ret);
    }

AIAENC_ERR5:
    s32Ret |= SAMPLE_COMM_AUDIO_StopAi(s_stAiCtx.AiDev, s_stAiCtx.s32AiChnCnt, HI_FALSE, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAi failed![%d]\n", s32Ret);
    }

AIAENC_ERR6:
    return s32Ret;
}

HI_S32 PLUG_MEDIA_AI_Start(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    if (s_stAiCtx.bInit)
    {
        if (!s_stAiCtx.bStart)
        {
            s_stAiCtx.bStart = HI_TRUE;
            s32Ret = pthread_create(&s_stAiCtx.tAiSendId, HI_NULL, MEDIA_AI_Thread, HI_NULL);
            if (HI_SUCCESS != s32Ret)
            {
                s_stAiCtx.bStart = HI_FALSE;
                LOGE("pthread create MEDIA_AI_Thread failed!\n");
            }
        }
    }
    
    return HI_SUCCESS;
}

HI_S32 PLUG_MEDIA_AI_Stop(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    
    if (s_stAiCtx.bInit && s_stAiCtx.bStart)
    {
        s_stAiCtx.bStart = HI_FALSE;
        pthread_join(s_stAiCtx.tAiSendId, HI_NULL);
    }
    
    return HI_SUCCESS;
}

HI_S32 PLUG_MEDIA_AI_Deinit(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_S32 i;
    
    if (!s_stAiCtx.bInit)
    {
        LOGE("ai has deinit\n");
        return HI_SUCCESS;
    }

    if (s_stAiCtx.bStart)
    {
        s32Ret = PLUG_MEDIA_AI_Stop();
        if (s32Ret != HI_SUCCESS)
        {
            LOGE("PLUG_MEDIA_AI_Stop[%d]\n", s32Ret);
        }
    }

    /* 停止音频相关输入以及编码 */
    for (i = 0; i < s_stAiCtx.s32AencChnCnt; i++)
    {
        s_stAiCtx.AeChn = i;
        s_stAiCtx.AiChn = i;

        s32Ret |= SAMPLE_COMM_AUDIO_AencUnbindAi(s_stAiCtx.AiDev, s_stAiCtx.AiChn, s_stAiCtx.AeChn);
        if (s32Ret != HI_SUCCESS)
        {
            LOGE("SAMPLE_COMM_AUDIO_AencUnbindAi\n");
        }
    }
    
    s32Ret |= SAMPLE_COMM_AUDIO_StopAenc(s_stAiCtx.s32AencChnCnt);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAenc failed![%d]\n", s32Ret);
    }

    s32Ret |= SAMPLE_COMM_AUDIO_StopAi(s_stAiCtx.AiDev, s_stAiCtx.s32AiChnCnt, HI_FALSE, HI_FALSE);
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAi failed![%d]\n", s32Ret);
    }
    
    s32Ret = HI_MPI_AENC_AacDeInit();
    if (s32Ret != HI_SUCCESS)
    {
        LOGE("SAMPLE_COMM_AUDIO_StopAi failed![%d]\n", s32Ret);
    }
    
    s_stAiCtx.bInit = HI_TRUE;
    
    return s32Ret;
}

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif
